/*
 * C/C++ KLIB Project
 * 
 * Copyright (c) 2012 Ken Zhang (cpp.china@gmail.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA
 *
 */

#include <process.h>
#include <windows.h>

#define MAX_THREAD_NUMBER	256

typedef struct _thread_t {
	HANDLE handle;
	int used;
	int done;
	int interval;
	int (*routine)(void *);
	void *arg;
} thread_t;

thread_t **_klibc_g_threads = NULL;

int thread_valid(int handle)
{
	if (_klibc_g_threads && handle >= 0 && handle < MAX_THREAD_NUMBER) {
		if (_klibc_g_threads[handle]) {
			return 1;
		}
	}
	return 0;
}

unsigned long __stdcall _thread_routine(void *arg)
{
	thread_t *context = (thread_t *)arg;
	int retcode;

	do {
		retcode = context->routine(context->arg);
		if (context->interval > 0) {
			Sleep(context->interval);
		}
	} while (!context->done && context->interval >= 0);
	
	context->done = 0;
	context->used = 0;

	return retcode;
}

int run(int (*routine)(void *), void *arg, int interval)
{
	int i;
	if (_klibc_g_threads == NULL) {
		_klibc_g_threads = calloc(MAX_THREAD_NUMBER, sizeof(thread_t *));
		for (i = 0; i < MAX_THREAD_NUMBER; i++) {
			_klibc_g_threads[i] = calloc(1, sizeof(thread_t));
		}
	}
	for (i = 0; i < MAX_THREAD_NUMBER; i++) {
		if (!_klibc_g_threads[i]->used) {
			_klibc_g_threads[i]->used = 1;
			_klibc_g_threads[i]->done = 0;
			_klibc_g_threads[i]->routine = routine;
			_klibc_g_threads[i]->arg = arg;
			_klibc_g_threads[i]->interval = interval;
			_klibc_g_threads[i]->handle = CreateThread(NULL, 0, _thread_routine, &_klibc_g_threads[i], 0, NULL);
			return i;
		}
	}
	return -1;
}

void stop(int handle, int wait)
{
	if (thread_valid(handle)) {
		if (_klibc_g_threads[handle]->used) {
			_klibc_g_threads[handle]->done = 1;
			if (wait) {
				WaitForSingleObject(_klibc_g_threads[handle]->handle, INFINITE);
			}
		}
	}
}

void twait(int handle, int timeout)
{
	if (thread_valid(handle)) {
		if (_klibc_g_threads[handle]->used) {
			WaitForSingleObject(_klibc_g_threads[handle]->handle, timeout);
		}
	}
}
